原本今天要把 UpdateUser 動作與頁面與側邊欄一起做完,不過時間來不及,所以先做 UpdateUser。
側邊欄我考慮很久,需不需要直接用文章來呈現,這樣照片、自介的位置可以自己編輯,甚至可以用編輯器插入 Html。不過想來想去有點麻煩,還是用 User 資料來實作。
所以資料庫的 User 資料表,我新增了 Image
與 About
兩個欄位。
public partial class User
{
......
public string Image { get; set; }
public string About { get; set; }
}
所以在 UserService 中的 Create 與 Update 都要做相應的調整。都要加上 Image
與 About
兩個欄位。
user.Image = viewModel.Image;
user.About = viewModel.About;
此外,因為我們的密碼有做雜湊,我在實做的過程中發現,UserViewModel 不能重複用在 Update 的時候。所以拆成 UserUpdateViewModel 和 UserCreateViewModel 。 UpdateUser 的時候不做變更密碼。
其中 Image
與 About
屬性設定為 string?
, 因為發現有 Nullable 設定的關係, string 需要明確宣告 string?
才可以空白不填,後面轉換成 User 時再設為 string.Empty 就可以了。
public class UserCreateViewModel : UserBaseViewModel
{
[Required]
[DataType(DataType.Password)]
public string Password { get; set; } = string.Empty;
[Compare(nameof(Password))]
[DataType(DataType.Password)]
public string PasswordCheck { get; set; } = string.Empty;
}
public class UserUpdateViewModel : UserBaseViewModel
{
}
public class UserBaseViewModel : IValidatableObject
{
public int Id { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; } = string.Empty;
[Required]
public string Name { get; set; } = string.Empty;
[Required]
public string DisplayId { get; set; } = string.Empty;
public string? Image { get; set; }
public string? About { get; set; }
public int Role { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Email))
{
yield break;
}
var userRepository = validationContext.GetService<IUserRepository>();
var emailUser = userRepository.Query(x => x.Email == Email).FirstOrDefault();
if (emailUser is not null && emailUser.Id != Id)
{
yield return new ValidationResult("此 Email 已存在", new[] { nameof(Email) });
}
var displayIdUser = userRepository.Query(x => x.DisplayId == DisplayId).FirstOrDefault();
if (displayIdUser is not null && displayIdUser.Id != Id)
{
yield return new ValidationResult("此 DisplayId 已存在", new[] { nameof(DisplayId) });
}
}
}
在 User 與 ViewModel 轉換的方面,我改用擴充方法來實作轉換
public static User ToUser(this UserCreateViewModel model)
{
var user = new User();
user.Id = model.Id;
user.Name = model.Name;
user.Email = model.Email;
user.Password = model.Password;
user.DisplayId = model.DisplayId;
user.Image = model.Image ?? string.Empty;
user.About = model.About ?? string.Empty;
user.Role = model.Role;
user.CreateDate = new DateTime();
user.UpdateDate = user.CreateDate;
return user;
}
public static UserUpdateViewModel ToUserUpdateViewModel(this User user)
{
var model = new UserUpdateViewModel();
model.Id = user.Id;
model.Name = user.Name;
model.Email = user.Email;
model.DisplayId = user.DisplayId;
model.About = user.About;
model.Image = user.Image;
model.Role = user.Role;
return model;
}
在 AdminController 下面實作 UpdateUser Action 如下,目前做了登入功能,所以 UserId 可以從 Cookie 拿取。
public async Task<IActionResult> UpdateUser()
{
var user = await _userService.GetAsync(User.GetUserId());
return View(user.ToUserUpdateViewModel());
}
[HttpPost]
public async Task<IActionResult> UpdateUser(UserUpdateViewModel user)
{
user.Id = User.GetUserId();
await _userService.UpdateAsync(user);
return RedirectToAction("Index", "Home");
}
我在 ClaimsPrincipal
型別上面,定義了擴充方法,來幫助我取得 Cookie 中的 UserId
public static class ClaimsPrincipalExtenstions
{
public static int GetUserId(this ClaimsPrincipal claims)
{
if (claims.Identity.IsAuthenticated)
{
var idStr = claims.FindFirstValue(nameof(User.Id));
if (int.TryParse(idStr, out int id))
{
return id;
}
}
return 0;
}
}
最後檢視的部分一樣使用樣板產生器產生。
最後實際程式碼以 GitHub 上為主。